﻿using Sys.Core.Models.Dto;
using Sys.Core.Models.Queries;
using Sys.Core.Models.VO;
using Sys.Core.Providers;
using MiniFox;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Infrastructure;
using MiniFox.Platform.Applications.Models;
using System.Reflection;
using System;
using MiniFox.Utility;


namespace Sys.Data
{
    [AutoResolve]
    public class AppDataProvider : EntityDataProvider<PlatformDbContext>, IApplicationProvider
    {
        private ApplicationVo MappingToVo(SysApp sysApp)
        {
            if (sysApp == null) return null;
            return new ApplicationVo
            {
                ApplicationID = sysApp.AppId,
                ApplicationName = sysApp.AppName,
                ApplicationDomain = sysApp.Host,
                RequireSSL = sysApp.Ssl,
                PortNumber = sysApp.Port,
                ApplicationPath = sysApp.Path,
                Icon = sysApp.Icon,
                CreateTime = sysApp.CreateTime,
                UpdateTime = sysApp.UpdateTime
            };
        }
        public ResultObject<IEnumerable<ApplicationVo>> Query(PagedQueryObject<AppQuery> request)
        {
            var result = this.PagedQueryExecute<SysApp>(request);
            return result.GetResultObject(a => new ApplicationVo
            {
                ApplicationID = a.AppId,
                ApplicationName = a.AppName,
                ApplicationDomain = a.Host,
                RequireSSL = a.Ssl,
                PortNumber = a.Port,
                ApplicationPath = a.Path,
                Icon = a.Icon,
                Enabled = a.Enabled,
                CreateTime = a.CreateTime,
                UpdateTime = a.UpdateTime
            });
        }

        public ApplicationVo GetApplication(string appId)
        {
            var sysApp = this.CurrentDbContext.SysApp.SingleOrDefault(a => a.AppId == appId);
            return this.MappingToVo(sysApp);
        }

        public bool CreateApplication(ApplicationDto application, string secretKey)
        {
            var sysApp = new SysApp();
            sysApp.AppId = application.ApplicationID;
            sysApp.AppName = application.ApplicationName;
            sysApp.Ssl = application.RequireSSL ?? false;
            sysApp.Host = application.ApplicationDomain;
            sysApp.Port = application.PortNumber;
            sysApp.Path = application.ApplicationPath;
            sysApp.Icon = application.Icon;
            sysApp.Enabled = true;
            sysApp.CreateTime = DateTime.Now;
            sysApp.UpdateTime = DateTime.Now;

            var appSecret = new SysAppSecret
            {
                AppId = sysApp.AppId,
                SecretKey = secretKey,
                SignatureKey = Tools.GenerateRandomCode(16),
            };

            var sysMenu = new SysMenu();
            sysMenu.MenuId = SequenceGUID.NewGUID();
            sysMenu.AppId = sysApp.AppId;
            sysMenu.ParentId = sysApp.AppId;
            sysMenu.MenuName = sysApp.AppName;
            sysMenu.MenuPath = $"/{sysApp.AppName}";
            sysMenu.Icon = sysApp.Icon;
            sysMenu.Ordinal = 0;
            return this.Insert(sysApp, appSecret, sysMenu) > 0;
        }

        public bool UpdateApplication(ApplicationDto application)
        {
            var sysApp = this.CurrentDbContext.SysApp.SingleOrDefault(a => a.AppId == application.ApplicationID);
            sysApp.AppName = application.ApplicationName;
            sysApp.Ssl = application.RequireSSL ?? false;
            sysApp.Host = application.ApplicationDomain;
            sysApp.Port = application.PortNumber;
            sysApp.Path = application.ApplicationPath;
            sysApp.Icon = application.Icon;
            sysApp.UpdateTime = DateTime.Now;

            //var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.AppId == m.ParentId && m.AppId == application.ApplicationID);
            //sysMenu.MenuName = sysApp.AppName; 
            return this.Update(sysApp) > 0;
        }

        public bool DeleteApplication(params string[] apps)
        {
            List<object> list = new List<object>();
            foreach(var appId in apps)
            {
                //var moduleArray = (from m in this.CurrentDbContext.SysModule where m.AppId == appId select m).ToArray();
                //list.AddRange(moduleArray);
                var app = this.CurrentDbContext.SysApp.SingleOrDefault(a => a.AppId == appId);
                list.Add(app);
            }
            return this.Delete(list.ToArray()) > 0;
        }
        public bool ToggleApplication(string appId)
        {
            var app = this.CurrentDbContext.SysApp.SingleOrDefault(a => a.AppId == appId);
            app.Enabled = !app.Enabled;
            return this.Update(app) > 0;
        }


        #region secret
        public string GetSecretKey(string appId)
        {
            var appSecret = this.CurrentDbContext.SysAppSecret.SingleOrDefault(a => a.AppId == appId);
            return appSecret.SecretKey;
        }
        public void SetSecretKey(string appId, string secretKey)
        {
            var appSecret = this.CurrentDbContext.SysAppSecret.SingleOrDefault(a => a.AppId == appId);
            appSecret.SecretKey = secretKey;
            appSecret.SignatureKey = Tools.GenerateRandomCode(16);
            this.Update(appSecret);
        }
        #endregion

        #region module
        public ResultObject<IEnumerable<ModuleVo>> QueryModules(PagedQueryObject<ModuleQuery> request)
        {
            var queryable = (from m in this.CurrentDbContext.SysModule
                             join a in this.CurrentDbContext.SysApp on m.AppId equals a.AppId
                             select new
                             {
                                 m.ModuleId,
                                 m.ModuleName,
                                 m.IsPublic,
                                 m.AppId,
                                 m.ModuleUri,
                                 m.QueryString,
                                 m.CreateTime,
                                 m.UpdateTime,
                                 m.Enabled,
                                 m.Icon,
                                 m.OpenMode,
                                 a.Ssl,
                                 a.AppName,
                                 a.Host,
                                 a.Port,
                                 a.Path
                             });
            var result = this.PagedQueryExecute(queryable, request);
            return result.GetResultObject(m => new ModuleVo
            {
                ModuleID = m.ModuleId,
                ModuleName = m.ModuleName,
                ApplicationID = m.AppId,
                ApplicationDomain = m.Host,
                ApplicationPath = m.Path,
                Icon = m.Icon,
                CreateTime = m.CreateTime,
                UpdateTime = m.UpdateTime,
                IsPublic = m.IsPublic,
                Enabled = m.Enabled,
                OpenMode = Enum.Parse<OpenMode>(m.OpenMode),
                PortNumber = m.Port,
                QueryString = m.QueryString,
                RequireSSL = m.Ssl,
                Uri = m.ModuleUri,
                Actions = (from ma in this.CurrentDbContext.SysModuleAction
                           where ma.ModuleId == m.ModuleId
                           select new ModuleActionVo
                           {
                               ActionID = ma.ActionId,
                               ActionName = ma.ActionName,
                               Uri = ma.ActionUri,
                               ApplicationDomain = m.Host,
                               ApplicationPath = m.Path,
                               PortNumber = m.Port,
                               RequireSSL = m.Ssl,
                               CreateTime = ma.CreateTime,
                               UpdateTime = ma.UpdateTime
                           }).ToArray()
            });
        }

        public ModuleVo GetModule(string moduleId)
        {
            var m = this.CurrentDbContext.SysModule.SingleOrDefault(t => t.ModuleId == moduleId);
            var a = this.CurrentDbContext.SysApp.SingleOrDefault(t => t.AppId == m.AppId);
            return new ModuleVo
            {
                ModuleID = m.ModuleId,
                ModuleName = m.ModuleName,
                ApplicationID = m.AppId,
                ApplicationDomain = a.Host,
                ApplicationPath = a.Path,
                Icon = m.Icon,
                CreateTime = m.CreateTime,
                UpdateTime = m.UpdateTime,
                IsPublic = m.IsPublic,
                Enabled = m.Enabled,
                OpenMode = Enum.Parse<OpenMode>(m.OpenMode),
                PortNumber = a.Port,
                QueryString = m.QueryString,
                RequireSSL = a.Ssl,
                Uri = m.ModuleUri,
                Actions = (from ma in this.CurrentDbContext.SysModuleAction
                           where ma.ModuleId == m.ModuleId
                           select new ModuleActionVo
                           {
                               ActionID = ma.ActionId,
                               ActionName = ma.ActionName,
                               Uri = ma.ActionUri,
                               ApplicationDomain = a.Host,
                               ApplicationPath = a.Path,
                               PortNumber = a.Port,
                               RequireSSL = a.Ssl,
                               CreateTime = ma.CreateTime,
                               UpdateTime = ma.UpdateTime
                           }).ToArray()
            };
        }

        public bool CreateModule(ModuleDto module)
        {
            var m = new SysModule();
            m.ModuleId = module.ModuleID;
            m.AppId = module.ApplicationID;
            m.ModuleName = module.ModuleName;
            m.ModuleUri = module.Uri;
            m.IsPublic = module.IsPublic;
            m.Enabled = module.Enabled;
            m.QueryString = module.QueryString;
            m.Icon = module.Icon;
            m.CreateTime = DateTime.Now;
            m.UpdateTime = DateTime.Now;
            m.Enabled = true;
            m.OpenMode = module.OpenMode.ToString();

            return this.Insert(m) > 0;
        }

        public bool UpdateModule(ModuleDto module)
        {
            var m = this.CurrentDbContext.SysModule.SingleOrDefault(t => t.ModuleId == module.ModuleID);

            m.AppId = module.ApplicationID;
            m.ModuleName = module.ModuleName;
            m.ModuleUri = module.Uri;
            m.IsPublic = module.IsPublic;
            m.Enabled = module.Enabled;
            m.QueryString = module.QueryString;
            m.Icon = module.Icon;
            m.UpdateTime = DateTime.Now;
            m.Enabled = module.Enabled;
            m.OpenMode = module.OpenMode.ToString();

            return this.Update(m) > 0;
        }

        public bool DeleteModule(params string[] modules)
        {
            var array = (from m in this.CurrentDbContext.SysModule where modules.Contains(m.ModuleId) select m).ToArray();
            return this.Delete(array) > 0;
        }

        public bool CreateModuleAction(ModuleActionDto moduleAction)
        {
            var m = new SysModuleAction();
            m.ModuleId = moduleAction.ModuleID;
            m.ActionId = moduleAction.ActionID;
            m.ActionName = moduleAction.ActionName;
            m.ActionUri = moduleAction.Uri;
            m.CreateTime = DateTime.Now;
            m.UpdateTime = DateTime.Now;

            return this.Insert(m) > 0;
        }

        public bool UpdateModuleAction(ModuleActionDto moduleAction)
        {
            var m = this.CurrentDbContext.SysModuleAction.SingleOrDefault(t => t.ActionId == moduleAction.ActionID);

            m.ActionId = moduleAction.ActionID;
            m.ActionName = moduleAction.ActionName;
            m.ActionUri = moduleAction.Uri;
            m.UpdateTime = DateTime.Now;

            return this.Update(m) > 0;
        }

        public bool DeleteModuleAction(string[] moduleActions)
        {
            var array = (from m in this.CurrentDbContext.SysModuleAction where moduleActions.Contains(m.ActionId) select m).ToArray();
            return this.Delete(array) > 0;
        }

        public ModuleActionVo GetModuleAction(string actionId)
        {
            var action = this.CurrentDbContext.SysModuleAction.SingleOrDefault(a => a.ActionId == actionId);
            var moduleVo = this.GetModule(action.ModuleId);

            return new ModuleActionVo
            {
                ActionID = action.ActionId,
                ActionName = action.ActionName,
                Uri = action.ActionUri,
                ApplicationDomain = moduleVo.ApplicationDomain,
                ApplicationPath = moduleVo.ApplicationPath,
                PortNumber = moduleVo.PortNumber,
                RequireSSL = moduleVo.RequireSSL,
                CreateTime = action.CreateTime,
                UpdateTime = action.UpdateTime
            };
        }

        public IEnumerable<ModuleActionVo> GetModuleActions(string moduleId)
        {
            var query = (from ma in this.CurrentDbContext.SysModuleAction
                         join m in this.CurrentDbContext.SysModule on ma.ModuleId equals m.ModuleId
                         join a in this.CurrentDbContext.SysApp on m.AppId equals a.AppId
                         where ma.ModuleId == moduleId
                         select new ModuleActionVo
                         {
                             ActionID = ma.ActionId,
                             ActionName = ma.ActionName,
                             Uri = ma.ActionUri,
                             ApplicationDomain = a.Host,
                             ApplicationPath = a.Path,
                             PortNumber = a.Port,
                             RequireSSL = a.Ssl,
                             CreateTime = ma.CreateTime,
                             UpdateTime = ma.UpdateTime
                         });
            return query.ToArray();
        }

        #endregion

    }
}
